﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.SessionState;

namespace Top.Redis.Session
{
    /// <summary>
    /// HttpSessionStateBase Redis实现
    /// </summary>
    public class HttpSessionByRedis : HttpSessionStateBase,IObserver<string>
    {
        private Dictionary<string, object> _items;

        /// <summary>
        /// 分布式Session前缀
        /// </summary>
        public static string SessionPrefix
        {
            get;
            set;
        }

        static HttpSessionByRedis()
        {
            SessionPrefix = Top.Core.Configs.AppSettings["SessionPrefix"];

            if (!string.IsNullOrEmpty(SessionPrefix))
            {
                SessionPrefix = SessionPrefix + ".";
            }

            RedisProvider.SetConfig("SessionMaster", "SessionSlave");
        }

        /// <summary>
        /// 获取会话的唯一标识符
        /// </summary>
        /// <returns>唯一会话标识符</returns>
        //public override string SessionID
        //{
        //    get
        //    {
        //        return CreateSessionId();
        //    }
        //}

        /// <summary>
        /// 会话的唯一标识符
        /// </summary>
        public new string SessionID { get; private set; }

        public new bool IsNewSession { get; private set; }

        public new int Timeout { get; set; }

        public event EventHandler OnAbandoned;

        private string CreateSessionId()
        {
            return Guid.NewGuid().ToString("n") + "." + new object().GetHashCode();
        }

        /// <summary>
        /// 获取或设置当前会话的字符集标识符
        /// </summary>
        public override int CodePage
        {
            get
            {
                return base.CodePage;
            }
            set
            {
                base.CodePage = value;
            }
        }

        /// <summary>
        /// 获取对当前会话状态对象的引用
        /// </summary>
        /// <returns>当前会话状态对象</returns>
        public override HttpSessionStateBase Contents
        {
            get
            {
                return base.Contents;
            }
        }

        /// <summary>
        /// 获取一个值，该值指示是否为无 Cookie 会话配置应用程序
        /// </summary>
        /// <returns>指示是否为无 Cookie 会话配置应用程序的 Cookie 模式值之一</returns>
        public override HttpCookieMode CookieMode
        {
            get
            {
                return base.CookieMode;
            }
        }

        /// <summary>
        /// 获取会话状态集合中的项数
        /// </summary>
        /// <returns>集合中的项数</returns>
        public override int Count
        {
            get
            {
                return _items.Count;
            }
        }

        /// <summary>
        /// 获取一个值，该值指示会话 ID 是否嵌入在 URL 中
        /// </summary>
        /// <returns>如果会话 ID 嵌入在 URL 中，则为 true；否则为 false</returns>
        public override bool IsCookieless
        {
            get
            {
                return base.IsCookieless;
            }
        }

        /// <summary>
        /// 获取一个值，该值指示会话是否是在当前请求过程中创建的
        /// </summary>
        /// <returns>如果会话是在当前请求过程中创建的，则为 true；否则为 false</returns>
        //public override bool IsNewSession
        //{
        //    get
        //    {
        //        return base.IsNewSession;
        //    }
        //}

        /// <summary>
        /// 获取一个值，该值指示会话是否为只读
        /// </summary>
        /// <returns>如果会话为只读，则为 true；否则为 false</returns>
        public override bool IsReadOnly
        {
            get
            {
                return base.IsReadOnly;
            }
        }

        /// <summary>
        /// 获取一个值，该值指示对会话状态值集合的访问是否是同步的（线程安全）
        /// </summary>
        /// <returns>如果对该集合的访问是同步的（线程安全），则为 true；否则，为 false</returns>
        public override bool IsSynchronized
        {
            get
            {
                return base.IsSynchronized;
            }
        }

        /// <summary>
        /// 获取存储在会话状态集合中的所有值的密钥集合
        /// </summary>
        /// <returns>会话密钥</returns>
        //public override NameObjectCollectionBase.KeysCollection Keys
        //{
        //    get
        //    {
        //        return base.Keys;
        //    }
        //}

        public new ICollection<string> Keys
        {
            get { return _items.Keys; }
        }

        /// <summary>
        /// 获取或设置当前会话的区域设置标识符 (LCID)
        /// </summary>
        /// <returns>当前会话的 LCID（区域性）</returns>
        public override int LCID
        {
            get
            {
                return base.LCID;
            }
            set
            {
                base.LCID = value;
            }
        }

        /// <summary>
        /// 获取当前会话状态模式
        /// </summary>
        /// <returns>会话状态模式</returns>
        public override SessionStateMode Mode
        {
            get
            {
                return SessionStateMode.Custom;
            }
        }

        /// <summary>
        /// 获取由 object 元素声明的对象的集合，这些元素在应用程序的 Global.asax 文件中标记为服务器控件且范围限定为当前会话
        /// </summary>
        /// <returns>在 Global.asax 文件中声明的对象</returns>
        public override HttpStaticObjectsCollectionBase StaticObjects
        {
            get
            {
                return base.StaticObjects;
            }
        }

        /// <summary>
        /// 获取一个对象，该对象可用于同步对会话状态值集合的访问
        /// </summary>
        /// <returns>可用于同步集合访问的对象</returns>
        public override object SyncRoot
        {
            get
            {
                return base.SyncRoot;
            }
        }

        /// <summary>
        /// 获取和设置在会话状态提供程序结束会话之前在请求之间所允许间隔的时间量（以分钟为单位）
        /// </summary>
        /// <returns>超时期限（以分钟为单位）</returns>
        //public override int Timeout
        //{
        //    get
        //    {
        //        return base.Timeout;
        //    }
        //    set
        //    {
        //        base.Timeout = value;
        //    }
        //}

        /// <summary>
        /// 通过使用指定的索引获取或设置会话值
        /// </summary>
        /// <param name="index">会话值的索引</param>
        /// <returns>存储在指定的索引处的会话状态值</returns>
        //public override object this[int index]
        //{
        //    get
        //    {
        //        return base[index];
        //    }
        //    set
        //    {
        //        base[index] = value;
        //    }
        //}

        /// <summary>
        /// 通过使用指定的名称获取或设置会话值
        /// </summary>
        /// <param name="key">会话值的键名</param>
        /// <returns>具有指定名称的会话状态值，如果该项不存在，则为 null</returns>
        public override object this[string key]
        {
            get
            {
                if (key == null || key.Length == 0 || key[0] == '!')
                {
                    return null;
                }
                object value;
                if (_items.TryGetValue(key, out value))
                {
                    return value;
                }

                try
                {
                    string rv = RedisProvider.HashGetSync("Session." + SessionPrefix + SessionID, key);

                    if (string.IsNullOrEmpty(rv))
                    {
                        value = rv;
                    }
                    else
                    {
                        //value = Json.ToObject(rv);
                    }

                    RedisProvider.HashSetSync("Session." + SessionPrefix + SessionID, key, rv);
                }
                catch (Exception ex)
                {
                    Trace.Write(ex);
                }

                return _items[key] = value;
            }
            set
            {
                if (key == null || key.Length == 0 || key[0] == '!')
                {
                    throw new ArgumentOutOfRangeException("Key的格式不正确");
                }
                if (value == null)
                {
                    Remove(key);
                }
                else
                {
                    _items[key] = value;
                }
            }
        }


        /// <summary>
        /// 取消当前会话
        /// </summary>
        public override void Abandon()
        {
            try
            {
                RedisProvider.SessionKeyDelSync("Session." + SessionPrefix + SessionID);
            }
            catch (Exception ex)
            {
                Trace.Write(ex);
            }

            _items.Clear();
            var temp = OnAbandoned;

            if (temp != null)
            {
                temp(this, EventArgs.Empty);
            }

            SessionID = CreateSessionId();
            IsNewSession = true;
        }

        /// <summary>
        /// 将一个项添加到会话状态集合
        /// </summary>
        /// <param name="name">要添加到会话状态集合的项的名称</param>
        /// <param name="value">要添加到会话状态集合的项的值</param>
        public override void Add(string name, object value)
        {
            base.Add(name, value);
        }

        /// <summary>
        /// 从会话状态集合中移除所有键和值
        /// </summary>
        public override void Clear()
        {
            base.Clear();
        }

        /// <summary>
        /// 从数组中的指定索引处开始，将会话状态值的集合复制到一个一维数组中
        /// </summary>
        /// <param name="array">要向其中复制会话值的数组</param>
        /// <param name="index">array 中开始复制位置的从零开始的索引</param>
        public override void CopyTo(Array array, int index)
        {
            base.CopyTo(array, index);
        }

        /// <summary>
        /// 返回一个枚举数，该枚举数可用于读取当前会话中的所有会话状态变量名称
        /// </summary>
        /// <returns>可以循环访问会话状态集合中的变量名称的枚举数</returns>
        public override IEnumerator GetEnumerator()
        {
            return _items.GetEnumerator();
        }

        /// <summary>
        /// 从会话状态集合中删除一个项
        /// </summary>
        /// <param name="name"></param>
        public override void Remove(string name)
        {
            _items.Remove(name);
        }

        /// <summary>
        /// 从会话状态集合中移除所有键和值
        /// </summary>
        public override void RemoveAll()
        {
            base.RemoveAll();
        }

        /// <summary>
        /// 从会话状态集合中删除位于指定索引处的项
        /// </summary>
        /// <param name="index">要从会话状态集合中移除的项的索引</param>
        public override void RemoveAt(int index)
        {
            base.RemoveAt(index);
        }

        public void OnCompleted()
        {
            var timeout = Timeout;

            if (timeout == 0)
            {
                timeout = 3600 * 2; //两小时过期
            }

            try
            {
                if (_items != null && _items.Count > 0)
                {
                    RedisProvider.HashSetSync("Session." + SessionPrefix + SessionID, _items, new TimeSpan(0, 0, timeout));
                }
            }
            catch (Exception ex)
            {
                Trace.Write(ex);
            }
        }

        public void OnError(Exception error)
        {

        }

        public void OnNext(string sessionid)
        {
            if (sessionid != null)
            {
                try
                {
                    bool isExists = RedisProvider.SessionKeyExistSync("Session." + SessionPrefix + sessionid);

                    if (isExists)
                    {
                        _items = new Dictionary<string, object>();
                        SessionID = sessionid;
                        return;
                    }
                }
                catch (Exception ex)
                {
                    Trace.Write(ex);
                }
            }

            _items = new Dictionary<string, object>();
            SessionID = CreateSessionId();
            IsNewSession = true;
        }
    }
}
